Variable Scopes and Shadowing

Understand variable scopes and shadowing in Go, along with an overview of zero values.

A scope is part of the program in which a variable can be seen. In Go, we have the following variable scopes:

  • Package scoped: Can be seen by the entire package and is declared outside a function

  • Function scoped: Can be seen within {} which defines the function

  • Statement scoped: Can be seen within {} of a statement in a function (for loop, if/else)

svg viewer

In the following program, the word variable is declared at the package level. It can be used by any function defined in the package:

Variable declared at package level

In the following program, the word variable is defined inside the main() function and can only be used inside {} which defines main. Outside, it is undefined:

Variable declared in main

Finally, in this program, i is statement scoped. It can be used on the line starting our for loop and inside {} of the loop, but it doesn't exist outside the loop:

Variable declared within the for loop

The best way to think of this is that if our variable is declared on a line that has { or within a set of {}, it can only be seen within those {}.

Redeclare a variable in the same scope#

The rule for this is that we cannot declare two variables with the same name within the same scope. This means that no two variables within the same scope can have the same name:

Two variables with the same name cause a declaration error

This program is invalid and will generate a compile error. Once we have declared the word variable, we cannot recreate it within the same scope. We can change the value to a new value, but we cannot create a second variable with the same name.

To assign word a new value, simply remove var from the line. The var says create variable where we want to only do an assignment:

Changing the value of the variable after it has been declared

Next, we'll look at what happens when we declare two variables with the same name in the same scope, but within separate code blocks.

Variable shadowing#

Variable shadowing occurs when a variable that is within our variable scope, but not in our local scope, is redeclared. This causes the local scope to lose access to the outer scoped variable:

Variable shadowing

As we can see, word is declared at the package level. But inside main, we define a new word variable, which overshadows the package level variable. When we refer to word now, we are using the one defined inside main().

The function printOutter() is called, but it doesn't have a locally shadowed word variable (one declared between its {}), so it used the one at the package level. This is one of the more common bugs for Go developers.

Zero values#

In some older languages, a variable declaration without an assignment has an unknown value. This is because the program creates a place in memory to store the value but doesn't put anything in it. So, the bits representing the value are set to whatever happened to be in that memory space before we created the variable.

This has led to many unfortunate bugs. So, in Go, declaring a variable without an assignment automatically assigns a value called the zero value. Here is a list of the zero values for Go types:

Type

Description

(u)int/8/16/32/64

The integer 0

bool

false

string

" "

float32/64

0.0

slice

The nil value

map

The nil value

struct

An empty struct, { }, with all the attributes having zero values

interface

The nil value

pointers

The nil value

channels

The nil value

byte

The integer 0

rune

The integer 0

array

A zero-length array

function

The nil value

Now that we understand what zero values are, let's see how Go prevents unused variables in our code.

Function or statement variable must be used#

The rule here is that if we create a variable within a function or statement, it must be used. This is much for the same reason as package imports; declaring a variable that isn't used is almost always a mistake.

This can be relaxed in much the same way as an import, using _, but is far less common.

Using underscore to assign values

The code above is explained as follows:

  • Line 1: This assigns the value stored in someVar to nothing.

  • Line 2: This assigns the value returned by someFunc() to nothing.

  • Line 3: Most commonly used when a function returns multiple values, but we only need one. Here, we create and assign to the needed variable, but the second value isn't something we use, so we drop it.

This lesson has provided knowledge of Go's basic types, the different ways to declare a variable, the rules around variable scopes and shadows, and Go's zero values.

Using Go's Variable Types

Looping in Go